<?php

namespace App\Console\Commands;

use Illuminate\Console\GeneratorCommand;

class MakeDefaultClass extends GeneratorCommand
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = "make:default-class {author?}";

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Create a new Controller、Model、Logic、Request、Search and Basic method';

    /**
     * Directory name
     * @var null
     */
    private $directory = null;

    /**
     * Class name
     * @var null
     */
    private $className = null;

    /**
     * Primary Key
     * @var null
     */
    private $primaryKey = null;

    /**
     * Table Name
     * @var null
     */
    private $tableName = null;

    /**
     * class path
     * @var array
     */
    private $classPath = [];

    /**
     * is Create
     * @var array
     */
    private $isCreate = [];

    /**
     * author
     * @var string
     */
    private $author = '';

    /**
     * Execute the console command.
     * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
     */
    public function handle()
    {
        $this->author = $this->argument('author') ?? 'yashuai<1762910894@qq.com>';
        $this->directory = $this->inputConfirmation('Directory name');
        $this->directory = str_replace(' ', '', ucwords($this->directory));
        if ($this->isReservedName($this->directory)) {
            $this->error('The name "' . $this->getNameInput() . '" is reserved by PHP.');
            return false;
        }

        $this->className = trim($this->ask('Class Name(If it is empty, the directory name is used)：'));
        $this->className = empty($this->className) ? $this->directory : str_replace(' ', '', ucwords($this->className));

        $createAll = $this->choice("Please select the generation method：", ['all', 'custom'], 'all') === 'all';
        if ($createAll) {
            $classList = ['Controller', 'Logic', 'Model', 'Search', 'RequestSave', 'RequestID'];
            foreach ($classList as $class) {
                $this->createValidator($class);
            }
        } else {
            $classList = ['Controller', 'Logic', 'Model', 'Search'];
            foreach ($classList as $class) {
                $this->createChoiceValidator($class);
            }

        }
        if ($this->isCreate['Model']) {
            $this->tableName  = $this->inputConfirmation('databases table name');
            $this->primaryKey = $this->inputConfirmation('table primary key name');

            if (!$createAll) {
                if ($this->createChoiceValidator('RequestSave')) {
                    $classList[] = 'RequestSave';
                }

                if ($this->createChoiceValidator('RequestID')) {
                    $classList[] = 'RequestID';
                }
            }
        }

        foreach ($classList as $class) {
            if ($this->isCreate[$class]) {
                $this->createClassValidator($class);
            }
        }

        return true;
    }

    /**
     * @Desc actions before class creation
     * @param string $type
     * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
     * @author yashuai<1762910894@qq.com>
     */
    public function createClassValidator($type = '')
    {
        switch ($type) {
            case 'Controller':
                $stub = $this->isCreate['Logic'] ? 'controller-method' : 'controller';
                $this->createClass($stub, $this->classPath[$type]);
                break;
            case 'Logic':
                $stub = $this->isCreate['Model'] ? 'logic-method' : 'logic';
                $this->createClass($stub, $this->classPath[$type]);
                break;
            case 'Model':
            case 'Search':
            case 'RequestID':
            case 'RequestSave':
                $this->createClass($type, $this->classPath[$type]);
                break;
            default:
                break;
        }
    }

    /**
     * @Desc create choice validator
     * @param $class
     * @return mixed
     * @author yashuai<1762910894@qq.com>
     */
    public function createChoiceValidator($class)
    {
        $this->isCreate[$class]  = false;
        $this->classPath[$class] = $this->getClassPath($class);
        if (!$this->files->exists($this->classPath[$class])) {
            $this->isCreate[$class] = $this->choice("Whether to create a {$class}？", ['yes', 'no'], 'yes') === 'yes';
        } else {
            $this->info("{$class} already exists, no need to create");
        }
        return $this->isCreate[$class];
    }

    /**
     * @Desc  create validator
     * @param $class
     * @return mixed
     * @author yashuai<1762910894@qq.com>
     */
    public function createValidator($class)
    {
        $this->classPath[$class] = $this->getClassPath($class);
        if (!$this->files->exists($this->classPath[$class])) {
            $this->isCreate[$class] = true;
        } else {
            $this->isCreate[$class] = false;
            $this->info("{$class} already exists, no need to create");
        }
        return $this->isCreate[$class];
    }

    /**
     * @Desc create class
     * @param string $stub
     * @param string $path
     * @return bool
     * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
     * @author yashuai<1762910894@qq.com>
     */
    public function createClass($stub = '', $path = '')
    {
        $file = $this->replaceKeyword($this->getStub($stub));
        if (!empty($file)) {
            $this->makeDirectory($path);
            $this->files->put($path, $file);
            $this->info("{$stub} created successfully");
        }
        return true;
    }

    /**
     * @Desc input confirmation
     * @param string $tips
     * @param int $num
     * @return string
     * @author yashuai<1762910894@qq.com>
     */
    public function inputConfirmation($tips = '', $num = 2)
    {
        $result = trim($this->ask("{$tips}："));
        if (empty($result)) {
            $num--;
            if (empty($num)) {
                $this->error("{$tips} cannot be empty");
                exit;
            }
            return $this->inputConfirmation($tips, $num);
        }
        return $result;
    }

    /**
     * @param string $type
     * @return string
     * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
     * @author yashuai<1762910894@qq.com>
     */
    protected function getStub($type = '')
    {
        $path = $this->laravel->basePath("stubs/" . strtolower($type) . ".stub");
        if (!$this->files->exists($path)) {
            $this->error("stubs/" . strtolower($type) . ".stub file missing, Creation failed");
            return false;
        }
        return $this->files->get($path);
    }

    /**
     * @Desc replace Keyword
     * @param string $stub
     * @return string|string[]
     * @author yashuai<1762910894@qq.com>
     */
    protected function replaceKeyword($stub)
    {
        if (empty($stub)) {
            return false;
        }
        $search  = ['{{ directory }}', '{{ className }}', '{{ tableName }}', '{{ primaryKey }}', '{{ author }}'];
        $replace = [$this->directory, $this->className, $this->tableName, $this->primaryKey, $this->author];
        return str_replace($search, $replace, $stub);
    }

    /**
     * @Desc get class path
     * @param string $type
     * @return string
     * @author yashuai<1762910894@qq.com>
     */
    private function getClassPath($type = '')
    {
        $path = '';
        switch ($type) {
            case 'Controller':
                $path = "Controllers/{$this->directory}/{$this->className}Controller.php";
                break;
            case 'Logic':
                $path = "Logic/{$this->directory}/{$this->className}Logic.php";
                break;
            case 'Model':
                $path = "Model/{$this->directory}/{$this->className}Model.php";
                break;
            case 'Search':
                $path = "Search/{$this->directory}/{$this->className}Search.php";
                break;
            case 'RequestID':
                $path = "Requests/{$this->directory}/{$this->className}IDRequest.php";
                break;
            case 'RequestSave':
                $path = "Requests/{$this->directory}/{$this->className}SaveRequest.php";
                break;
        }
        return $this->laravel->basePath('app/Http/' . $path);
    }
}
